package mgo

import (
	"reflect"
	"sync"
	"time"

	"gopkg.in/mgo.v2"
	"strings"
	"hub.hddznet.com/app/framework/app"
	"hub.hddznet.com/app/framework/tools"
	"hub.hddznet.com/app/framework/app/engines"
)

func InitMongo(dbs interface{}) {
	initMongo(dbs, true)
}

// 此方法为内部使用方法，不建议在外部项目中调用
func InitMongoWithoutRegist(dbs interface{}) {
	initMongo(dbs, false)
}

func initMongo(dbs interface{}, flag bool) {
	tools.RftStructDeep(dbs, func(t1 reflect.Type, v1 reflect.Value, tg reflect.StructTag) {
		if tg.Get("json") == "prefix" {
			return
		}
		if t1.Kind() == reflect.Ptr {
			objvl := reflect.New(t1.Elem())
			v1.Set(objvl)
		} else {
			objvl := reflect.New(t1)
			v1.Set(objvl.Elem())
		}
		return
	})
	db := new(MgoDatabase)
	tools.RftStructDeep(dbs, func(t1 reflect.Type, v1 reflect.Value, tg reflect.StructTag) {
		if tg.Get("db") != "" && t1.String() == "*mgo.MgoDatabase" {
			db = new(MgoDatabase)
			dbstr := tg.Get("db")
			db.Database = registerMongo(dbstr)
			v1.Set(reflect.ValueOf(db))
			return
		}
		if tg.Get("db") != "" && t1.String() == "*mgo.MgoFs" {
			dbfs := new(MgoFs)
			dbstr := tg.Get("db")
			dbfs.GridFS = registerMongo(dbstr).GridFS("fs")
			v1.Set(reflect.ValueOf(dbfs))
			return
		}

		if tg.Get("json") != "" && t1.String() == "*mgo.MgoCollection" {
			collection := new(MgoCollection)
			dbstr := tg.Get("json")
			collection.Collection = db.C(dbstr)
			mgoIndexGenerate(tg.Get("mgoIdx"), collection.Collection)
			v1.Set(reflect.ValueOf(collection))
		}
		return
	})

	// 初始化完成之后，自动注册到mgo服务中
	if flag {
		engines.RegistMgo(dbs)
	}
}

func mgoIndexGenerate(str string, col *mgo.Collection) {
	if str == "" {
		return
	}
	strs := strings.Split(str, ";")
	for _, s := range strs {
		uni := false
		if strings.Contains(s, "unique") {
			uni = true
			strings.Replace(s, "unique", "", 10)
		}

		i := mgo.Index{
			Key:        strings.Split(s, ","),
			Unique:     uni,
			DropDups:   true,
			Background: true,
			Sparse:     true,
		}
		col.EnsureIndex(i)
	}
}

//初始化mongo引擎
var mutex = sync.Mutex{}

func registerMongo(db string) *mgo.Database {
	mutex.Lock()
	defer mutex.Unlock()

	url := app.MustValue("database", "mongodb", "")
	if url == "" {
		panic("未定义数据库...")
	}
	sess, err := mgo.Dial(url)
	if err != nil {
		panic(err)
	}
	sess.SetSyncTimeout(time.Second * 10)
	sess.SetPoolLimit(5000)

	//保证连接可用性 - 断线自动重连
	go func(sess *mgo.Session) {
		for {
			select {
			case <-time.After(5 * time.Second):
				sess.Refresh()
			}
		}
	}(sess)
	prefix := app.MustValue("database", "mongodb.prefix", "")
	return sess.DB(prefix + db)

}
